/*
silverPDF is sponsored by Aleyant Systems (http://www.aleyant.com)

silverPDF is based on PdfSharp (http://www.pdfsharp.net) and iTextSharp (http://itextsharp.sourceforge.net)

Developers: Ai_boy (aka Oleksii Okhrymenko)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above information and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR SPONSORS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

*/
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Runtime.CompilerServices;
using System.Collections;
using System.util;
using System.Reflection;

using iTextSharp.text.pdf;
using iTextSharp.text.pdf.codec;

namespace iTextSharp.text {
    /// <summary>
    /// An Image is the representation of a graphic element (JPEG, PNG or GIF)
    /// that has to be inserted into the document
    /// </summary>
    /// <seealso cref="T:iTextSharp.text.Element"/>
    /// <seealso cref="T:iTextSharp.text.Rectangle"/>
    public abstract class Image : Rectangle {
    
        // static membervariables (concerning the presence of borders)
    
        /// <summary> this is a kind of image Element. </summary>
        public const int DEFAULT = 0;
    
        /// <summary> this is a kind of image Element. </summary>
        public const int RIGHT_ALIGN = 2;
    
        /// <summary> this is a kind of image Element. </summary>
        public const int LEFT_ALIGN = 0;
    
        /// <summary> this is a kind of image Element. </summary>
        public const int MIDDLE_ALIGN = 1;
    
        /// <summary> this is a kind of image Element. </summary>
        public const int TEXTWRAP = 4;
    
        /// <summary> this is a kind of image Element. </summary>
        public const int UNDERLYING = 8;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int AX = 0;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int AY = 1;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int BX = 2;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int BY = 3;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int CX = 4;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int CY = 5;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int DX = 6;
    
        /// <summary> This represents a coordinate in the transformation matrix. </summary>
        public const int DY = 7;
    
        /** type of image */
        public const int ORIGINAL_NONE = 0;

        /** type of image */
        public const int ORIGINAL_JPEG = 1;

        /** type of image */
        public const int ORIGINAL_PNG = 2;

        /** type of image */
        public const int ORIGINAL_GIF = 3;

        /** type of image */
        public const int ORIGINAL_BMP = 4;

        /** type of image */
        public const int ORIGINAL_TIFF = 5;

        /** type of image */
        public const int ORIGINAL_WMF = 6;

	    /** type of image */
	    public const int ORIGINAL_JPEG2000 = 8;

	    /**
	    * type of image
	    * @since	2.1.5
	    */
	    public const int ORIGINAL_JBIG2 = 9;

        /** Image color inversion */
        protected bool invert = false;
    
        /// <summary> The imagetype. </summary>
        protected int type;
    
        /// <summary> The URL of the image. </summary>
        protected Uri url;
    
        /// <summary> The raw data of the image. </summary>
        protected byte[] rawData;
    
        /// <summary> The template to be treated as an image. </summary>
        //protected PdfTemplate[] template = new PdfTemplate[1];
    
        /// <summary> The alignment of the Image. </summary>
        protected int alignment;
    
        /// <summary> Text that can be shown instead of the image. </summary>
        protected string alt;
    
        /// <summary> This is the absolute X-position of the image. </summary>
        protected float absoluteX = float.NaN;
    
        /// <summary> This is the absolute Y-position of the image. </summary>
        protected float absoluteY = float.NaN;
    
        /// <summary> This is the width of the image without rotation. </summary>
        protected float plainWidth;
    
        /// <summary> This is the width of the image without rotation. </summary>
        protected float plainHeight;
    
        /// <summary> This is the scaled width of the image taking rotation into account. </summary>
        protected float scaledWidth;
    
        /// <summary> This is the original height of the image taking rotation into account. </summary>
        protected float scaledHeight;
    
        /**
        * The compression level of the content streams.
        * @since   2.1.3
        */
        protected int compressionLevel = -1;

        /// <summary> This is the rotation of the image. </summary>
        protected float rotationRadians;
    
        /// <summary> this is the colorspace of a jpeg-image. </summary>
        protected int colorspace = -1;
    
        /// <summary> this is the bits per component of the raw image. It also flags a CCITT image.</summary>
        protected int bpc = 1;
    
        /// <summary> this is the transparency information of the raw image</summary>
        protected int[] transparency;
    
        // for the moment these variables are only used for Images in class Table
        // code contributed by Pelikan Stephan
        /** the indentation to the left. */
        protected float indentationLeft = 0;

        /** the indentation to the right. */
        protected float indentationRight = 0;
        // serial stamping
    
        protected long mySerialId = GetSerialId();
    
        static object serialId = 0L;

        /// <summary> Holds value of property dpiX. </summary>
        protected int dpiX = 0;
    
        /// <summary> Holds value of property dpiY. </summary>
        protected int dpiY = 0;
    
        protected bool mask = false;
    
        protected Image imageMask;
    
        /// <summary> Holds value of property interpolation. </summary>
        protected bool interpolation;
    
        /// <summary> if the annotation is not null the image will be clickable. </summary>
        //protected Annotation annotation = null;

        /// <summary> ICC Profile attached </summary>
        protected ICC_Profile profile = null;
    
        /** Holds value of property deflated. */
        protected bool deflated = false;

        private PdfSharp.Pdf.PdfDictionary additional = null;

        /** Holds value of property smask. */
        private bool smask;

        /** Holds value of property XYRatio. */
        private float xyRatio = 0;

        /** Holds value of property originalType. */
        protected int originalType = ORIGINAL_NONE;

        /** Holds value of property originalData. */
        protected byte[] originalData;

        /** The spacing before the image. */
        protected float spacingBefore;

        /** The spacing after the image. */
        protected float spacingAfter;

        /**
        * Holds value of property widthPercentage.
        */
        private float widthPercentage = 100;

        //protected IPdfOCG layer;

        /**
        * Holds value of property initialRotation.
        */
        private float initialRotation;
        
        //private PdfIndirectReference directReference;

        // constructors
    
        /// <summary>
        /// Constructs an Image-object, using an url.
        /// </summary>
        /// <param name="url">the URL where the image can be found.</param>
        public Image(Uri url) : base(0, 0) {
            this.url = url;
            this.alignment = DEFAULT;
            rotationRadians = 0;
        }

        /// <summary>
        /// Constructs an Image object duplicate.
        /// </summary>
        /// <param name="image">another Image object.</param>
        public Image(Image image) : base(image) {
            this.type = image.type;
            this.url = image.url;
            this.alignment = image.alignment;
            this.alt = image.alt;
            this.absoluteX = image.absoluteX;
            this.absoluteY = image.absoluteY;
            this.plainWidth = image.plainWidth;
            this.plainHeight = image.plainHeight;
            this.scaledWidth = image.scaledWidth;
            this.scaledHeight = image.scaledHeight;
            this.rotationRadians = image.rotationRadians;
            this.indentationLeft = image.indentationLeft;
            this.indentationRight = image.indentationRight;
            this.colorspace = image.colorspace;
            this.rawData = image.rawData;
            //this.template = image.template;
            this.bpc = image.bpc;
            this.transparency = image.transparency;
            this.mySerialId = image.mySerialId;
            this.invert = image.invert;
            this.dpiX = image.dpiX;
            this.dpiY = image.dpiY;
            this.mask = image.mask;
            this.imageMask = image.imageMask;
            this.interpolation = image.interpolation;
            //this.annotation = image.annotation;
            this.profile = image.profile;
            this.deflated = image.deflated;
            this.additional = image.additional;
            this.smask = image.smask;
            this.XYRatio = image.XYRatio;
            this.originalData = image.originalData;
            this.originalType = image.originalType;
            this.spacingAfter = image.spacingAfter;
            this.spacingBefore = image.spacingBefore;
            this.widthPercentage = image.widthPercentage;
            //this.layer = image.layer;
            this.initialRotation = image.initialRotation;
            //this.directReference = image.directReference;
        }
    
        /// <summary>
        /// Gets an instance of an Image.
        /// </summary>
        /// <param name="image">an Image</param>
        /// <returns>an object of type Gif, Jpeg or Png</returns>
        public static Image GetInstance(Image image) {
            if (image == null)
                return null;
            return (Image)image.GetType().GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(Image)}, null).Invoke(new object[] {image});
        }

        /// <summary>
        /// Gets an instance of an Image.
        /// </summary>
        /// <param name="url">an URL</param>
        /// <returns>an object of type Gif, Jpeg or Png</returns>
        //public static Image GetInstance(Uri url) {
        //    Stream istr = null;
        //    try {
        //        WebRequest w = WebRequest.Create(url);
        //        istr = w.GetResponse().GetResponseStream();
        //        int c1 = istr.ReadByte();
        //        int c2 = istr.ReadByte();
        //        int c3 = istr.ReadByte();
        //        int c4 = istr.ReadByte();
        //        // jbig2
        //        int c5 = istr.ReadByte();
        //        int c6 = istr.ReadByte();
        //        int c7 = istr.ReadByte();
        //        int c8 = istr.ReadByte();
        //        istr.Close();

        //        istr = null;
        //        if (c1 == 'G' && c2 == 'I' && c3 == 'F') {
        //            GifImage gif = new GifImage(url);
        //            Image img = gif.GetImage(1);
        //            return img;
        //        }
        //        if (c1 == 0xFF && c2 == 0xD8) {
        //            return new Jpeg(url);
        //        }
        //        if (c1 == 0x00 && c2 == 0x00 && c3 == 0x00 && c4 == 0x0c) {
        //            return new Jpeg2000(url);
        //        }
        //        if (c1 == 0xff && c2 == 0x4f && c3 == 0xff && c4 == 0x51) {
        //            return new Jpeg2000(url);
        //        }
        //        if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1]
        //                && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) {
        //            Image img = PngImage.GetImage(url);
        //            return img;
        //        }
        //        //if (c1 == 0xD7 && c2 == 0xCD) {
        //        //    Image img = new ImgWMF(url);
        //        //    return img;
        //        //}
        //        if (c1 == 'B' && c2 == 'M') {
        //            Image img = BmpImage.GetImage(url);
        //            return img;
        //        }
        //        if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42)
        //                || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) {
        //            RandomAccessFileOrArray ra = null;
        //            try {
        //                if (url.IsFile) {
        //                    String file = url.LocalPath;
        //                    ra = new RandomAccessFileOrArray(file);
        //                } else
        //                    ra = new RandomAccessFileOrArray(url);
        //                Image img = TiffImage.GetTiffImage(ra, 1);
        //                img.url = url;
        //                return img;
        //            } finally {
        //                if (ra != null)
        //                    ra.Close();
        //            }

        //        }
        //        if ( c1 == 0x97 && c2 == 'J' && c3 == 'B' && c4 == '2' &&
        //                c5 == '\r' && c6 == '\n' && c7 == 0x1a && c8 == '\n' ) {
        //            RandomAccessFileOrArray ra = null;
        //            try {
        //                if (url.IsFile) {
        //                    String file = url.LocalPath;
        //                    ra = new RandomAccessFileOrArray(file);
        //                } else
        //                    ra = new RandomAccessFileOrArray(url);
        //                Image img = JBIG2Image.GetJbig2Image(ra, 1);
        //                img.url = url;
        //                return img;
        //            } finally {
        //                if (ra != null)
        //                    ra.Close();
        //            }
        //        }
        //        throw new IOException(url.ToString()
        //                + " is not a recognized imageformat.");
        //    } finally {
        //        if (istr != null) {
        //            istr.Close();
        //        }
        //    }
        //}

        public static Image GetInstance(Stream s) {
            byte[] a = RandomAccessFileOrArray.InputStreamToArray(s);
            s.Close();
            return GetInstance(a);
        }

        /**
        * Creates a JBIG2 Image.
        * @param   width   the width of the image
        * @param   height  the height of the image
        * @param   data    the raw image data
        * @param   globals JBIG2 globals
        * @since   2.1.5
        */
        public static Image GetInstance(int width, int height, byte[] data, byte[] globals) {
            Image img = new ImgJBIG2(width, height, data, globals);
            return img;
        }
        
        /// <summary>
        /// Gets an instance of an Image.
        /// </summary>
        /// <param name="img">a byte array</param>
        /// <returns>an object of type Gif, Jpeg or Png</returns>
        public static Image GetInstance(byte[] imgb) {
            int c1 = imgb[0];
            int c2 = imgb[1];
            int c3 = imgb[2];
            int c4 = imgb[3];

            if (c1 == 'G' && c2 == 'I' && c3 == 'F') {
                GifImage gif = new GifImage(imgb);
                return gif.GetImage(1);
            }
            if (c1 == 0xFF && c2 == 0xD8) {
                return new Jpeg(imgb);
            }
			if (c1 == 0x00 && c2 == 0x00 && c3 == 0x00 && c4 == 0x0c) {
				return new Jpeg2000(imgb);
			}
			if (c1 == 0xff && c2 == 0x4f && c3 == 0xff && c4 == 0x51) {
				return new Jpeg2000(imgb);
			}
            if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1]
                    && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) {
                return PngImage.GetImage(imgb);
            }
            //if (c1 == 0xD7 && c2 == 0xCD) {
            //    return new ImgWMF(imgb);
            //}
            if (c1 == 'B' && c2 == 'M') {
                return BmpImage.GetImage(imgb);
            }
            if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42)
                    || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) {
                RandomAccessFileOrArray ra = null;
                try {
                    ra = new RandomAccessFileOrArray(imgb);
                    Image img = TiffImage.GetTiffImage(ra, 1);
                    if (img.OriginalData == null)
                        img.OriginalData = imgb;

                    return img;
                } finally {
                    if (ra != null)
                        ra.Close();
                }

            }
            throw new IOException("The byte array is not a recognized imageformat.");
        }
        /// <summary>
        /// Converts a .NET image to a Native(PNG, JPG, GIF, WMF) image
        /// </summary>
        /// <param name="image"></param>
        /// <param name="?"></param>
        /// <returns></returns>
        //public static Image GetInstance(System.Drawing.Image image, System.Drawing.Imaging.ImageFormat format) {
        //    MemoryStream ms = new MemoryStream();
        //    image.Save(ms, format);
        //    return GetInstance(ms.ToArray());
        //}
    
        /// <summary>
        /// Gets an instance of an Image from a System.Drwaing.Image.
        /// </summary>
        /// <param name="image">the System.Drawing.Image to convert</param>
        /// <param name="color">
        /// if different from null the transparency
        /// pixels are replaced by this color
        /// </param>
        /// <param name="forceBW">if true the image is treated as black and white</param>
        /// <returns>an object of type ImgRaw</returns>
        //public static Image GetInstance(System.Drawing.Image image, Color color, bool forceBW) {
        //    System.Drawing.Bitmap bm = (System.Drawing.Bitmap)image;
        //    int w = bm.Width;
        //    int h = bm.Height;
        //    int pxv = 0;
        //    if (forceBW) {
        //        int byteWidth = (w / 8) + ((w & 7) != 0 ? 1 : 0);
        //        byte[] pixelsByte = new byte[byteWidth * h];
            
        //        int index = 0;
        //        int size = h * w;
        //        int transColor = 1;
        //        if (color != null) {
        //            transColor = (color.R + color.G + color.B < 384) ? 0 : 1;
        //        }
        //        int[] transparency = null;
        //        int cbyte = 0x80;
        //        int wMarker = 0;
        //        int currByte = 0;
        //        if (color != null) {
        //            for (int j = 0; j < h; j++) {
        //                for (int i = 0; i < w; i++) {
        //                    int alpha = bm.GetPixel(i, j).A;
        //                    if (alpha < 250) {
        //                        if (transColor == 1)
        //                            currByte |= cbyte;
        //                    }
        //                    else {
        //                        if ((bm.GetPixel(i, j).ToArgb() & 0x888) != 0)
        //                            currByte |= cbyte;
        //                    }
        //                    cbyte >>= 1;
        //                    if (cbyte == 0 || wMarker + 1 >= w) {
        //                        pixelsByte[index++] = (byte)currByte;
        //                        cbyte = 0x80;
        //                        currByte = 0;
        //                    }
        //                    ++wMarker;
        //                    if (wMarker >= w)
        //                        wMarker = 0;
        //                }
        //            }
        //        }
        //        else {
        //            for (int j = 0; j < h; j++) {
        //                for (int i = 0; i < w; i++) {
        //                    if (transparency == null) {
        //                        int alpha = bm.GetPixel(i, j).A;
        //                        if (alpha == 0) {
        //                            transparency = new int[2];
        //                            transparency[0] = transparency[1] = ((bm.GetPixel(i, j).ToArgb() & 0x888) != 0) ? 1 : 0;
        //                        }
        //                    }
        //                    if ((bm.GetPixel(i, j).ToArgb() & 0x888) != 0)
        //                        currByte |= cbyte;
        //                    cbyte >>= 1;
        //                    if (cbyte == 0 || wMarker + 1 >= w) {
        //                        pixelsByte[index++] = (byte)currByte;
        //                        cbyte = 0x80;
        //                        currByte = 0;
        //                    }
        //                    ++wMarker;
        //                    if (wMarker >= w)
        //                        wMarker = 0;
        //                }
        //            }
        //        }
        //        return Image.GetInstance(w, h, 1, 1, pixelsByte, transparency);
        //    }
        //    else {
        //        byte[] pixelsByte = new byte[w * h * 3];
        //        byte[] smask = null;
            
        //        int index = 0;
        //        int size = h * w;
        //        int red = 255;
        //        int green = 255;
        //        int blue = 255;
        //        if (color != null) {
        //            red = color.R;
        //            green = color.G;
        //            blue = color.B;
        //        }
        //        int[] transparency = null;
        //        if (color != null) {
        //            for (int j = 0; j < h; j++) {
        //                for (int i = 0; i < w; i++) {
        //                    int alpha = (bm.GetPixel(i, j).ToArgb() >> 24) & 0xff;
        //                    if (alpha < 250) {
        //                        pixelsByte[index++] = (byte) red;
        //                        pixelsByte[index++] = (byte) green;
        //                        pixelsByte[index++] = (byte) blue;
        //                    }
        //                    else {
        //                        pxv = bm.GetPixel(i, j).ToArgb();
        //                        pixelsByte[index++] = (byte) ((pxv >> 16) & 0xff);
        //                        pixelsByte[index++] = (byte) ((pxv >> 8) & 0xff);
        //                        pixelsByte[index++] = (byte) ((pxv) & 0xff);
        //                    }
        //                }
        //            }
        //        }
        //        else {
        //            int transparentPixel = 0;
        //            smask = new byte[w * h];
        //            bool shades = false;
        //            int smaskPtr = 0;
        //            for (int j = 0; j < h; j++) {
        //                for (int i = 0; i < w; i++) {
        //                    pxv = bm.GetPixel(i, j).ToArgb();
        //                    byte alpha = smask[smaskPtr++] = (byte) ((pxv >> 24) & 0xff);
        //                    /* bugfix by Chris Nokleberg */
        //                    if (!shades) {
        //                        if (alpha != 0 && alpha != 255) {
        //                            shades = true;
        //                        } else if (transparency == null) {
        //                            if (alpha == 0) {
        //                                transparentPixel = pxv & 0xffffff;
        //                                transparency = new int[6];
        //                                transparency[0] = transparency[1] = (transparentPixel >> 16) & 0xff;
        //                                transparency[2] = transparency[3] = (transparentPixel >> 8) & 0xff;
        //                                transparency[4] = transparency[5] = transparentPixel & 0xff;
        //                            }
        //                        } else if ((pxv & 0xffffff) != transparentPixel) {
        //                            shades = true;
        //                        }
        //                    }
        //                    pixelsByte[index++] = (byte) ((pxv >> 16) & 0xff);
        //                    pixelsByte[index++] = (byte) ((pxv >> 8) & 0xff);
        //                    pixelsByte[index++] = (byte) (pxv & 0xff);
        //                }
        //            }
        //            if (shades)
        //                transparency = null;
        //            else
        //                smask = null;
        //        }
        //        Image img = Image.GetInstance(w, h, 3, 8, pixelsByte, transparency);
        //        if (smask != null) {
        //            Image sm = Image.GetInstance(w, h, 1, 8, smask);
        //            sm.MakeMask();
        //            img.ImageMask = sm;
        //        }
        //        return img;
        //    }
        //}
    
        /// <summary>
        /// Gets an instance of an Image from a System.Drawing.Image.
        /// </summary>
        /// <param name="image">the System.Drawing.Image to convert</param>
        /// <param name="color">
        /// if different from null the transparency
        /// pixels are replaced by this color
        /// </param>
        /// <returns>an object of type ImgRaw</returns>
        //public static Image GetInstance(System.Drawing.Image image, Color color) {
        //    return Image.GetInstance(image, color, false);
        //}
    
        /// <summary>
        /// Gets an instance of an Image.
        /// </summary>
        /// <param name="filename">a filename</param>
        /// <returns>an object of type Gif, Jpeg or Png</returns>
        //public static Image GetInstance(string filename) {
        //    return GetInstance(Utilities.ToURL(filename));
        //}
    
        /// <summary>
        /// Gets an instance of an Image in raw mode.
        /// </summary>
        /// <param name="width">the width of the image in pixels</param>
        /// <param name="height">the height of the image in pixels</param>
        /// <param name="components">1,3 or 4 for GrayScale, RGB and CMYK</param>
        /// <param name="bpc">bits per component</param>
        /// <param name="data">the image data</param>
        /// <returns>an object of type ImgRaw</returns>
        public static Image GetInstance(int width, int height, int components, int bpc, byte[] data) {
            return Image.GetInstance(width, height, components, bpc, data, null);
        }
    
        /**
        * Reuses an existing image.
        * @param ref the reference to the image dictionary
        * @throws BadElementException on error
        * @return the image
        */    
        //public static Image GetInstance(PRIndirectReference iref) {
        //    PdfDictionary dic = (PdfDictionary)PdfReader.GetPdfObjectRelease(iref);
        //    int width = ((PdfNumber)PdfReader.GetPdfObjectRelease(dic.Get(PdfName.WIDTH))).IntValue;
        //    int height = ((PdfNumber)PdfReader.GetPdfObjectRelease(dic.Get(PdfName.HEIGHT))).IntValue;
        //    Image imask = null;
        //    PdfObject obj = dic.Get(PdfName.SMASK);
        //    if (obj != null && obj.IsIndirect()) {
        //        imask = GetInstance((PRIndirectReference)obj);
        //    }
        //    else {
        //        obj = dic.Get(PdfName.MASK);
        //        if (obj != null && obj.IsIndirect()) {
        //            PdfObject obj2 = PdfReader.GetPdfObjectRelease(obj);
        //            if (obj2 is PdfDictionary)
        //                imask = GetInstance((PRIndirectReference)obj);
        //        }
        //    }
        //    Image img = new ImgRaw(width, height, 1, 1, null);
        //    img.imageMask = imask;
        //    img.directReference = iref;
        //    return img;
        //}
        
        /// <summary>
        /// Gets an instance of an Image in raw mode.
        /// </summary>
        /// <param name="template"></param>
        /// <returns></returns>
        //public static Image GetInstance(PdfTemplate template) {
        //    return new ImgTemplate(template);
        //}
    
        /// <summary>
        /// Gets an instance of an Image in raw mode.
        /// </summary>
        /// <param name="width">the width of the image in pixels</param>
        /// <param name="height">the height of the image in pixels</param>
        /// <param name="reverseBits"></param>
        /// <param name="typeCCITT"></param>
        /// <param name="parameters"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static Image GetInstance(int width, int height, bool reverseBits, int typeCCITT, int parameters, byte[] data) {
            return Image.GetInstance(width, height, reverseBits, typeCCITT, parameters, data, null);
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="reverseBits"></param>
        /// <param name="typeCCITT"></param>
        /// <param name="parameters"></param>
        /// <param name="data"></param>
        /// <param name="transparency"></param>
        /// <returns></returns>
        public static Image GetInstance(int width, int height, bool reverseBits, int typeCCITT, int parameters, byte[] data, int[] transparency) {
            if (transparency != null && transparency.Length != 2)
                throw new Exception("Transparency length must be equal to 2 with CCITT images");
            Image img = new ImgCCITT(width, height, reverseBits, typeCCITT, parameters, data);
            img.transparency = transparency;
            return img;
        }

        /// <summary>
        /// Gets an instance of an Image in raw mode.
        /// </summary>
        /// <param name="width">the width of the image in pixels</param>
        /// <param name="height">the height of the image in pixels</param>
        /// <param name="components">1,3 or 4 for GrayScale, RGB and CMYK</param>
        /// <param name="bpc">bits per component</param>
        /// <param name="data">the image data</param>
        /// <param name="transparency">
        /// transparency information in the Mask format of the
        /// image dictionary
        /// </param>
        /// <returns>an object of type ImgRaw</returns>
        public static Image GetInstance(int width, int height, int components, int bpc, byte[] data, int[] transparency) {
            if (transparency != null && transparency.Length != components * 2)
                throw new Exception("Transparency length must be equal to (componentes * 2)");
            if (components == 1 && bpc == 1) {
                byte[] g4 = CCITTG4Encoder.Compress(data, width, height);
                return Image.GetInstance(width, height, false, Element.CCITTG4, Element.CCITT_BLACKIS1, g4, transparency);
            }
            Image img = new ImgRaw(width, height, components, bpc, data);
            img.transparency = transparency;
            return img;
        }
    
        // methods to set information
    
        /// <summary>
        /// Sets the absolute position of the Image.
        /// </summary>
        /// <param name="absoluteX"></param>
        /// <param name="absoluteY"></param>
        public void SetAbsolutePosition(float absoluteX, float absoluteY) {
            this.absoluteX = absoluteX;
            this.absoluteY = absoluteY;
        }
    
        /// <summary>
        /// Scale the image to an absolute width and an absolute height.
        /// </summary>
        /// <param name="newWidth">the new width</param>
        /// <param name="newHeight">the new height</param>
        public void ScaleAbsolute(float newWidth, float newHeight) {
            plainWidth = newWidth;
            plainHeight = newHeight;
            float[] matrix = this.Matrix;
            scaledWidth = matrix[DX] - matrix[CX];
            scaledHeight = matrix[DY] - matrix[CY];
            WidthPercentage = 0;
        }
    
        /// <summary>
        /// Scale the image to an absolute width.
        /// </summary>
        /// <param name="newWidth">the new width</param>
        public void ScaleAbsoluteWidth(float newWidth) {
            plainWidth = newWidth;
            float[] matrix = this.Matrix;
            scaledWidth = matrix[DX] - matrix[CX];
            scaledHeight = matrix[DY] - matrix[CY];
            WidthPercentage = 0;
        }
    
        /// <summary>
        /// Scale the image to an absolute height.
        /// </summary>
        /// <param name="newHeight">the new height</param>
        public void ScaleAbsoluteHeight(float newHeight) {
            plainHeight = newHeight;
            float[] matrix = Matrix;
            scaledWidth = matrix[DX] - matrix[CX];
            scaledHeight = matrix[DY] - matrix[CY];
            WidthPercentage = 0;
        }
    
        /// <summary>
        /// Scale the image to a certain percentage.
        /// </summary>
        /// <param name="percent">the scaling percentage</param>
        public void ScalePercent(float percent) {
            ScalePercent(percent, percent);
        }
    
        /// <summary>
        /// Scale the width and height of an image to a certain percentage.
        /// </summary>
        /// <param name="percentX">the scaling percentage of the width</param>
        /// <param name="percentY">the scaling percentage of the height</param>
        public void ScalePercent(float percentX, float percentY) {
            plainWidth = (this.Width * percentX) / 100f;
            plainHeight = (this.Height * percentY) / 100f;
            float[] matrix = Matrix;
            scaledWidth = matrix[DX] - matrix[CX];
            scaledHeight = matrix[DY] - matrix[CY];
            WidthPercentage = 0;
        }
    
        /// <summary>
        /// Scales the image so that it fits a certain width and height.
        /// </summary>
        /// <param name="fitWidth">the width to fit</param>
        /// <param name="fitHeight">the height to fit</param>
        public void ScaleToFit(float fitWidth, float fitHeight) {
            ScalePercent(100);
            float percentX = (fitWidth * 100) / this.ScaledWidth;
            float percentY = (fitHeight * 100) / this.ScaledHeight;
            ScalePercent(percentX < percentY ? percentX : percentY);
            WidthPercentage = 0;
        }
    
        /**
        * Gets the current image rotation in radians.
        * @return the current image rotation in radians
        */
        public float GetImageRotation() {
            float rot = (float) ((rotationRadians - initialRotation) % (2.0 * Math.PI));
            if (rot < 0) {
                rot += (float)(2.0 * Math.PI);
            }
            return rot;
        }

        /// <summary>
        /// Sets the rotation of the image in radians.
        /// </summary>
        /// <param name="r">rotation in radians</param>
        public new float Rotation {
            set {
                double d=Math.PI;                  //__IDS__
                rotationRadians = (float) ((value + initialRotation) % (2.0 * d)); //__IDS__
                if (rotationRadians < 0) {
                    rotationRadians += (float)(2.0 * d);           //__IDS__
                }
                float[] matrix = Matrix;
                scaledWidth = matrix[DX] - matrix[CX];
                scaledHeight = matrix[DY] - matrix[CY];
            }
        }
    
        /// <summary>
        /// Sets the rotation of the image in degrees.
        /// </summary>
        /// <param name="deg">rotation in degrees</param>
        public float RotationDegrees {
            set {
                Rotation = (value / 180 * (float)Math.PI); //__IDS__
            }
        }
    
        /// <summary>
        /// Get/set the annotation.
        /// </summary>
        /// <value>the Annotation</value>
        //public Annotation Annotation {
        //    get {
        //        return annotation;
        //    }

        //    set {
        //        this.annotation = value;
        //    }
        //}
    
        // methods to retrieve information
    
        /// <summary>
        /// Gets the bpc for the image.
        /// </summary>
        /// <remarks>
        /// this only makes sense for Images of the type RawImage.
        /// </remarks>
        /// <value>a bpc value</value>
        public int Bpc {
            get {
                return bpc;
            }
        }
    
        /// <summary>
        /// Gets the raw data for the image.
        /// </summary>
        /// <remarks>
        /// this only makes sense for Images of the type RawImage.
        /// </remarks>
        /// <value>the raw data</value>
        public byte[] RawData {
            get {
                return rawData;
            }
        }
    
        /// <summary>
        /// Get/set the template to be used as an image.
        /// </summary>
        /// <remarks>
        /// this only makes sense for Images of the type ImgTemplate.
        /// </remarks>
        /// <value>the template</value>
        //public PdfTemplate TemplateData {
        //    get {
        //        return template[0];
        //    }

        //    set {
        //        this.template[0] = value;
        //    }
        //}
    
        /// <summary>
        /// Checks if the Images has to be added at an absolute position.
        /// </summary>
        /// <returns>a bool</returns>
        public bool HasAbsolutePosition() {
            return !float.IsNaN(absoluteY);
        }
    
        /// <summary>
        /// Checks if the Images has to be added at an absolute X position.
        /// </summary>
        /// <returns>a bool</returns>
        public bool HasAbsoluteX() {
            return !float.IsNaN(absoluteX);
        }
    
        /// <summary>
        /// Returns the absolute X position.
        /// </summary>
        /// <value>a position</value>
        public float AbsoluteX {
            get {
                return absoluteX;
            }
        }
    
        /// <summary>
        /// Returns the absolute Y position.
        /// </summary>
        /// <value>a position</value>
        public float AbsoluteY {
            get {
                return absoluteY;
            }
        }
    
        /// <summary>
        /// Returns the type.
        /// </summary>
        /// <value>a type</value>
        public override int Type {
            get {
                return type;
            }
        }
    
        /**
        * @see com.lowagie.text.Element#isNestable()
        * @since   iText 2.0.8
        */
        public override bool IsNestable() {
            return true;
        }

        /// <summary>
        /// Returns true if the image is a Jpeg-object.
        /// </summary>
        /// <returns>a bool</returns>
        public bool IsJpeg() {
            return type == Element.JPEG;
        }
    
        /// <summary>
        /// Returns true if the image is a ImgRaw-object.
        /// </summary>
        /// <returns>a bool</returns>
        public bool IsImgRaw() {
            return type == Element.IMGRAW;
        }

        /// <summary>
        /// Returns true if the image is an ImgTemplate-object.
        /// </summary>
        /// <returns>a bool</returns>
        public bool IsImgTemplate() {
            return type == Element.IMGTEMPLATE;
        }
    
        /// <summary>
        /// Gets the string-representation of the reference to the image.
        /// </summary>
        /// <value>a string</value>
        public Uri Url {
            get {
                return url;
            }
            set {
                url = value;
            }
        }
    
        /// <summary>
        /// Get/set the alignment for the image.
        /// </summary>
        /// <value>a value</value>
        public int Alignment {
            get {
                return alignment;
            }

            set {
                this.alignment = value;
            }
        }
    
        /// <summary>
        /// Get/set the alternative text for the image.
        /// </summary>
        /// <value>a string</value>
        public string Alt {
            get {
                return alt;
            }

            set {
                this.alt = value;
            }
        }
    
        /// <summary>
        /// Gets the scaled width of the image.
        /// </summary>
        /// <value>a value</value>
        public float ScaledWidth {
            get {
                return scaledWidth;
            }
        }
    
        /// <summary>
        /// Gets the scaled height of the image.
        /// </summary>
        /// <value>a value</value>
        public float ScaledHeight {
            get {
                return scaledHeight;
            }
        }
    
        /// <summary>
        /// Gets the colorspace for the image.
        /// </summary>
        /// <remarks>
        /// this only makes sense for Images of the type Jpeg.
        /// </remarks>
        /// <value>a colorspace value</value>
        public int Colorspace {
            get {
                return colorspace;
            }
        }
    
        /// <summary>
        /// Returns the transformation matrix of the image.
        /// </summary>
        /// <value>an array [AX, AY, BX, BY, CX, CY, DX, DY]</value>
        public float[] Matrix {
            get {
                float[] matrix = new float[8];
                float cosX = (float)Math.Cos(rotationRadians);
                float sinX = (float)Math.Sin(rotationRadians);
                matrix[AX] = plainWidth * cosX;
                matrix[AY] = plainWidth * sinX;
                matrix[BX] = (- plainHeight) * sinX;
                matrix[BY] = plainHeight * cosX;
                if (rotationRadians < Math.PI / 2f) {
                    matrix[CX] = matrix[BX];
                    matrix[CY] = 0;
                    matrix[DX] = matrix[AX];
                    matrix[DY] = matrix[AY] + matrix[BY];
                }
                else if (rotationRadians < Math.PI) {
                    matrix[CX] = matrix[AX] + matrix[BX];
                    matrix[CY] = matrix[BY];
                    matrix[DX] = 0;
                    matrix[DY] = matrix[AY];
                }
                else if (rotationRadians < Math.PI * 1.5f) {
                    matrix[CX] = matrix[AX];
                    matrix[CY] = matrix[AY] + matrix[BY];
                    matrix[DX] = matrix[BX];
                    matrix[DY] = 0;
                }
                else {
                    matrix[CX] = 0;
                    matrix[CY] = matrix[AY];
                    matrix[DX] = matrix[AX] + matrix[BX];
                    matrix[DY] = matrix[BY];
                }
                return matrix;
            }
        }
    
        /// <summary>
        /// Returns the transparency.
        /// </summary>
        /// <value>the transparency</value>
        public int[] Transparency {
            get {
                return transparency;
            }
            set {
                transparency = value;
            }
        }
    
        /// <summary>
        /// Gets the plain width of the image.
        /// </summary>
        /// <value>a value</value>
        public float PlainWidth {
            get {
                return plainWidth;
            }
        }
    
        /// <summary>
        /// Gets the plain height of the image.
        /// </summary>
        /// <value>a value</value>
        public float PlainHeight {
            get {
                return plainHeight;
            }
        }
    
        /// <summary>
        /// generates new serial id
        /// </summary>
        static protected long GetSerialId() {
            lock (serialId) {
                serialId = (long)serialId + 1L;
                return (long)serialId;
            }
        }
    
        /// <summary>
        /// returns serial id for this object
        /// </summary>
        public long MySerialId {
            get {
                return mySerialId;
            }
        }
    
        /// <summary>
        /// Gets the dots-per-inch in the X direction. Returns 0 if not available.
        /// </summary>
        /// <value>the dots-per-inch in the X direction</value>
        public int DpiX {
            get {
                return dpiX;
            }
        }
    
        /// <summary>
        /// Gets the dots-per-inch in the Y direction. Returns 0 if not available.
        /// </summary>
        /// <value>the dots-per-inch in the Y direction</value>
        public int DpiY {
            get {
                return dpiY;
            }
        }
    
        /**
        * Sets the dots per inch value
        * 
        * @param dpiX
        *            dpi for x coordinates
        * @param dpiY
        *            dpi for y coordinates
        */
        public void SetDpi(int dpiX, int dpiY) {
            this.dpiX = dpiX;
            this.dpiY = dpiY;
        }

        /// <summary>
        /// Returns true if this Image has the
        /// requisites to be a mask.
        /// </summary>
        /// <returns>true if this Image can be a mask</returns>
        public bool IsMaskCandidate() {
            if (type == Element.IMGRAW) {
                if (bpc > 0xff)
                    return true;
            }
            return colorspace == 1;
        }
    
        /// <summary>
        /// Make this Image a mask.
        /// </summary>
        public void MakeMask() {
            if (!IsMaskCandidate())
                throw new Exception("This image can not be an image mask.");
            mask = true;
        }
    
        /// <summary>
        /// Get/set the explicit masking.
        /// </summary>
        /// <value>the explicit masking</value>
        public Image ImageMask {
            get {
                return imageMask;
            }

            set {
                if (this.mask)
                    throw new Exception("An image mask cannot contain another image mask.");
                if (!value.mask)
                    throw new Exception("The image mask is not a mask. Did you do MakeMask()?");
                imageMask = value;
                smask = (value.bpc > 1 && value.bpc <= 8);
            }
        }
    
        /// <summary>
        /// Returns true if this Image is a mask.
        /// </summary>
        /// <returns>true if this Image is a mask</returns>
        public bool IsMask() {
            return mask;
        }
    
        /// <summary>
        /// Inverts the meaning of the bits of a mask.
        /// </summary>
        /// <value>true to invert the meaning of the bits of a mask</value>
        public bool Inverted {
            set {
                this.invert = value;
            }
            get {
                return this.invert;
            }
        }
    
        /// <summary>
        /// Sets the image interpolation. Image interpolation attempts to
        /// produce a smooth transition between adjacent sample values.
        /// </summary>
        /// <value>New value of property interpolation.</value>
        public bool Interpolation {
            set {
                this.interpolation = value;
            }
            get {
                return this.interpolation;
            }
        }
    
        /** Tags this image with an ICC profile.
         * @param profile the profile
         */    
        public ICC_Profile TagICC {
            get {
                return profile;
            }
            set {
                this.profile = value;
            }
        }
    
        /** Checks is the image has an ICC profile.
         * @return the ICC profile or null
         */    
        public bool HasICCProfile() {
            return (this.profile != null);
        }

        public bool Deflated {
            get {
                return deflated;
            }
            set {
                deflated = value;
            }
        }

        public PdfSharp.Pdf.PdfDictionary Additional
        {
            get {
                return additional;
            }
            set {
                additional = value;
            }
        }

        public bool Smask {
            get {
                return smask;
            }
            set {
                smask = value;
            }
        }

        public float XYRatio {
            get {
                return xyRatio;
            }
            set {
                xyRatio = value;
            }
        }

        public float IndentationLeft {
            get {
                return indentationLeft;
            }
            set {
                indentationLeft = value;
            }
        }

        public float IndentationRight {
            get {
                return indentationRight;
            }
            set {
                indentationRight = value;
            }
        }

        public int OriginalType {
            get {
                return originalType;
            }
            set {
                originalType = value;
            }
        }

        public byte[] OriginalData {
            get {
                return originalData;
            }
            set {
                originalData = value;
            }
        }

        public float SpacingBefore {
            get {
                return spacingBefore;
            }
            set {
                spacingBefore = value;
            }
        }

        public float SpacingAfter {
            get {
                return spacingAfter;
            }
            set {
                spacingAfter = value;
            }
        }

        public float WidthPercentage {
            get {
                return widthPercentage;
            }
            set {
                widthPercentage = value;
            }
        }

        //public IPdfOCG Layer {
        //    get {
        //        return layer;
        //    }
        //    set {
        //        layer = value;
        //    }
        //}

        private PdfObject SimplifyColorspace(PdfArray obj) {
            if (obj == null)
                return obj;
            PdfObject first = obj.GetAsName(0);
            if (PdfName.CALGRAY.Equals(first))
                return PdfName.DEVICEGRAY;
            else if (PdfName.CALRGB.Equals(first))
                return PdfName.DEVICERGB;
            else
                return obj;
        }

        /**
        * Replaces CalRGB and CalGray colorspaces with DeviceRGB and DeviceGray.
        */    
        //public void SimplifyColorspace() {
        //    if (additional == null)
        //        return;
        //    PdfArray value = additional.GetAsArray(PdfName.COLORSPACE);
        //    if (value == null)
        //        return;
        //    PdfObject cs = SimplifyColorspace(value);
        //    PdfObject newValue;
        //    if (cs.IsName())
        //        newValue = cs;
        //    else {
        //        newValue = value;
        //        PdfName first = value.GetAsName(0);
        //        if (PdfName.INDEXED.Equals(first)) {
        //            if (value.Size >= 2) {
        //                PdfArray second = value.GetAsArray(1);
        //                if (second != null) {
        //                    value[1] = SimplifyColorspace(second);
        //                }
        //            }
        //        }
        //    }
        //    additional.Put(PdfName.COLORSPACE, value);
        //}
        
        /**
        * Some image formats, like TIFF may present the images rotated that have
        * to be compensated.
        */
        public float InitialRotation {
            get {
                return initialRotation;
            }
            set {
                float old_rot = rotationRadians - this.initialRotation;
                this.initialRotation = value;
                Rotation = old_rot;
            }
        }

        //public PdfIndirectReference DirectReference {
        //    set {
        //        directReference = value;
        //    }
        //    get {
        //        return directReference;
        //    }
        //}

        /**
        * Sets the compression level to be used if the image is written as a compressed stream.
        * @param compressionLevel a value between 0 (best speed) and 9 (best compression)
        * @since   2.1.3
        */
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
        //public int CompressionLevel {
        //    set {
        //        if (compressionLevel < PdfStream.NO_COMPRESSION || compressionLevel > PdfStream.BEST_COMPRESSION)
        //            compressionLevel = PdfStream.DEFAULT_COMPRESSION;
        //        else
        //            compressionLevel = compressionLevel;
        //    }
        //    get {
        //        return compressionLevel;
        //    }
        //}
        public int CompressionLevel
        {
            get { return compressionLevel; }
            set
            {
                if (compressionLevel < 0 || compressionLevel > 9)
                {
                    compressionLevel = -1;
                }
            }
        }
    }
}
